`timescale 1ns / 1ps 
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 2022/02/26 15:54:49
// Design Name:
// Module Name: i2c_controller
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////


module i2c_controller(
           sys_clk,
           rst,
           finish,
           scl,
           sda,
           addr,
           data_in

       );

input sys_clk;
input rst;
output scl;
output finish;
inout sda;
output [8: 0] addr;
input [15: 0] data_in;

parameter _0 = 4'b0000;  //initial
parameter _1 = 4'b0001;  //rising
parameter _2 = 4'b0010;  //high
parameter _3 = 4'b0100;  //falling
parameter _4 = 4'b1000; //low

parameter device_address_wr = 8'h60; //device_address_wr
parameter addr_max = 9'd176;

wire [7: 0] data_in_addr = data_in[15: 8];
wire [7: 0] data_in_data = data_in[7: 0];
reg [3: 0] cycle_div = _0;
reg reg_scl = 1'b0;
reg reg_sda = 1'b1;
reg reg_finish = 1'b0;
reg [7: 0] data_out = 8'd0;
reg [3: 0] data_digit = 4'd0;
reg en_sda_tri = 1'b0; //0->output;1->input

reg [8: 0]cnt_scl = 9'd0;
reg [4: 0] state = 5'd0;
reg [19: 0] delay_cnt = 20'd0;
reg [8: 0] reg_addr = 9'd0;
reg [19: 0] cnt = 20'hfffff;
reg scl_sel = 1'b0;

assign scl = (rst && scl_sel) ? reg_scl : 1'b1;
assign sda = en_sda_tri ? 1'bz : reg_sda;
assign finish = reg_finish;
assign addr = reg_addr;

always@(posedge sys_clk or negedge rst)
	//cnt_scl_adder
	begin
		if (!rst)
			begin
				cnt_scl <= 9'd0;
			end
		else
			if (cnt_scl == 9'd499)
				cnt_scl <= 9'd0;
			else
				cnt_scl <= cnt_scl + 1'b1;
	end

always@(posedge sys_clk or negedge rst)
	//clock_div
	begin
		if (!rst)
			begin
				cycle_div <= _0;
			end
		else
			case (cnt_scl)
				9'd499:
					begin
						cycle_div <= _1;
						reg_scl <= 1'b1;
					end
				9'd124:
					begin
						cycle_div <= _2;
					end
				9'd249:
					begin
						cycle_div <= _3;
						reg_scl <= 1'b0;
					end
				9'd374:
					begin
						cycle_div <= _4;
					end
				default:
					begin
						cycle_div <= _0;
					end
			endcase
	end

always@(posedge sys_clk or negedge rst)
	begin
		if (!rst)
			begin
				state <= 5'd0;
				delay_cnt <= 20'd0;
				en_sda_tri <= 1'b0;
				reg_sda <= 1'b1;
				data_digit <= 4'd0;
				reg_addr <= 9'd0;
				cnt <= 20'hfffff;
				reg_finish <= 1'b0;
				scl_sel <= 1'b0;

			end
		else
			case (state)
				5'd0:
					begin
						//delay_1ms
						if (delay_cnt == 20'd49999)
							begin
								delay_cnt <= 20'd0;
								state <= state + 1'b1;
							end
						else
							delay_cnt <= delay_cnt + 1'b1;
					end
				5'd1:
					//start
					begin
						if (cycle_div == _2)
							begin
								en_sda_tri <= 1'b0;
								reg_sda <= 1'b0;
								state <= state + 1'b1;
								scl_sel <= 1'b1;
							end
					end
				5'd2:
					//device_address-prepare
					begin
						if (cycle_div == _3)
							begin
								data_out <= device_address_wr;
								state <= state + 1'b1;
							end
					end
				5'd3:
					begin
						if (cycle_div == _4)
							begin
								if (data_digit == 4'd8)
									begin
										state <= state + 1'b1;
										en_sda_tri <= 1'b1;
										data_digit <= 4'd0;
									end
								else
									case (data_digit)
										4'd0, 4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7:
											begin
												reg_sda <= data_out[7 - data_digit];
												data_digit = data_digit + 1'b1;
											end
									endcase
							end
					end
				5'd4:   //ack1
					begin
						if (cycle_div == _2)
							begin
								if (sda == 1'b0)
									begin
										state <= state + 1'b1;
										en_sda_tri <= 1'b0;
										reg_sda <= 1'b0;
									end
								else
									begin
										state <= 5'd1;
									end
							end

					end
				5'd5:   //data_in_1_to_data_out_1
					begin
						if (cycle_div == _3)
							begin
								data_out <= data_in_addr;
								state <= state + 1'b1;
							end
					end
				5'd6:  //data_out_1_to_reg_sda
					begin
						if (cycle_div == _4)
							begin
								if (data_digit == 4'd8)
									begin
										state <= state + 1'b1;
										en_sda_tri <= 1'b1;
										data_digit <= 4'd0;
									end
								else
									case (data_digit)
										4'd0, 4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7:
											begin
												reg_sda <= data_out[7 - data_digit];
												data_digit = data_digit + 1'b1;
											end
									endcase

							end
					end
				5'd7:  //ack2
					begin
						if (cycle_div == _2)
							begin
								if (sda == 1'b0)
									begin
										state <= state + 1'b1;
										en_sda_tri <= 1'b0;
										reg_sda <= 1'b1;
									end
								else
									begin
										state <= 5'd1;
									end

							end
					end
				5'd8:   //data_in_2_to_data_out_2
					if (cycle_div == _3)
						begin
							data_out <= data_in_data;
							state <= state + 1'b1;
						end
				5'd9:
					begin
						if (cycle_div == _4)
							begin
								if (data_digit == 4'd8)
									begin
										state <= state + 1'b1;
										en_sda_tri <= 1'b1;
										data_digit <= 4'd0;
									end
								else
									case (data_digit)
										4'd0, 4'd1, 4'd2, 4'd3, 4'd4, 4'd5, 4'd6, 4'd7:
											begin
												reg_sda <= data_out[7 - data_digit];
												data_digit = data_digit + 1'b1;
											end
									endcase
							end
					end
				5'd10:
					begin
						//ack3
						if (cycle_div == _2)
							begin
								if (sda == 1'b0)
									begin
										state <= state + 1'b1;
										en_sda_tri <= 1'b0;
										reg_sda <= 1'b0;
										reg_addr <= reg_addr + 1'b1;
									end
								else
									begin
										state <= 5'd1;
									end
							end
					end
				5'd11:  //stop
					begin
						if (cycle_div == _2)
							begin
								reg_sda <= 1'b1;
								if (reg_addr == 2'd3)
									begin
										state <= 5'd12;
										scl_sel <= 1'b0;
									end
								else
									if (reg_addr == addr_max + 1'b1)
										begin
											state <= 5'd13;
											scl_sel <= 1'b0;
											reg_finish <= 1'b1;
										end
									else
										state <= 5'd1;
							end
					end
				5'd12:  //delay-5ms
					begin
						if (cnt == 20'd249999)
							begin
								cnt <= 20'd0;
								state <= 5'd1;
								scl_sel <= 1'b1;
							end
						else
							cnt <= cnt + 1'b1;
					end
				5'd13:
					begin

					end
				default:
					begin

					end
			endcase
	end
endmodule
